package com.lizhen.core.function.genericity;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author lizhen
 */
public class Genericity<T> {
    static List<GenericityApple> apples = Arrays.asList(new GenericityApple());
    static List<GenericityFruit> fruit = Arrays.asList(new GenericityFruit());
    private T t;

    public static <K, V> boolean compare(GenericityMethod<K, V> g1, GenericityMethod<K, V> g2) {
        return g1.getKey().equals(g2.getKey()) && g1.getValue().equals(g2.getValue());
    }

    public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
        int count = 0;
        for (T e : anArray)
            if (e.compareTo(elem) > 0)
                ++count;
        return count;
    }

    public static void Main(String[] args) {
        Genericity<Integer> integerBox = new Genericity<Integer>();
        Genericity<Double> doubleBox = new Genericity<Double>();
        Genericity<String> stringBox = new Genericity<String>();
        // Boundary character
        Character[] arrs = {'a', 'b'};

        System.out.println(integerBox);
        System.out.println(doubleBox);
        System.out.println(stringBox);
        System.out.println(countGreaterThan(arrs, 'c'));
    }

    public static void main(String[] args) {
        // Generic method
        GenericityMethod.main(null);
        // PECS
        GenericityPECS.main(null);
        // Generic class
        Genericity.Main(null);
    }

    public void set(T t) {
        this.t = t;
    }

    public T get() {
        return t;
    }

    static class GenericityMethod<K, V> {
        private K key;
        private V value;

        public GenericityMethod(K key, V value) {
            this.key = key;
            this.value = value;
        }

        public static void main(String[] args) {
            GenericityMethod<Integer, String> gm1 = new GenericityMethod<Integer, String>(1, "apple");
            GenericityMethod<Integer, String> gm2 = new GenericityMethod<Integer, String>(2, "pear");
            // @Way1 Complete generic method invoke
            boolean gc1 = Genericity.<Integer, String>compare(gm1, gm2);
            // @Way2 Default generic method invoke
            boolean gc2 = Genericity.compare(gm1, gm2);

            System.out.println(gc1);
            System.out.println(gc2);
        }

        public K getKey() {
            return key;
        }

        public void setKey(K key) {
            this.key = key;
        }

        public V getValue() {
            return value;
        }

        public void setValue(V value) {
            this.value = value;
        }
    }

    static class GenericityFruit {
        public static void main(String[] args) {
        }
    }

    static class GenericityApple extends GenericityFruit {
        public static void main(String[] args) {
        }
    }

    static class GenericityPECS {
        static List<GenericityApple> apples = new ArrayList<GenericityApple>();
        static List<GenericityFruit> fruit = new ArrayList<GenericityFruit>();

        static <T> void writeExact(List<T> list, T item) {
            list.add(item);
        }

        static <T> void CovariantWriter(List<? super T> list, T item) {
            list.add(item);
        }

        static void get() {
            CovariantReader<GenericityFruit> fruitReader = new CovariantReader<GenericityFruit>();
            fruit.add(new GenericityFruit());
            apples.add(new GenericityApple());
            GenericityFruit f = fruitReader.readCovariant(fruit);
            GenericityFruit a = fruitReader.readCovariant(apples);

            System.out.println(f);
            System.out.println(a);
        }

        static void add() {
            writeExact(apples, new GenericityApple());
            writeExact(fruit, new GenericityApple());
            CovariantWriter(apples, new GenericityApple());
            CovariantWriter(fruit, new GenericityApple());
        }

        public static void main(String[] args) {
            get();
            add();

            System.out.println(apples);
            System.out.println(fruit);
        }

        static class CovariantReader<T> {
            T readCovariant(List<? extends T> list) {
                return list.get(0);
            }
        }
    }
}

